package com.airbnb.epoxy;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;

class DataBindingProcessor {
  private final Elements elementUtils;
  private final Types typeUtils;
  private final ErrorLogger errorLogger;
  private final ConfigManager configManager;
  private final LayoutResourceProcessor layoutResourceProcessor;
  private final DataBindingModuleLookup dataBindingModuleLookup;
  private final GeneratedModelWriter modelWriter;
  private final List<DataBindingModelInfo> modelInfoList = new ArrayList<>();

  DataBindingProcessor(Elements elementUtils, Types typeUtils, ErrorLogger errorLogger,
      ConfigManager configManager, LayoutResourceProcessor layoutResourceProcessor,
      DataBindingModuleLookup dataBindingModuleLookup, GeneratedModelWriter modelWriter) {

    this.elementUtils = elementUtils;
    this.typeUtils = typeUtils;
    this.errorLogger = errorLogger;
    this.configManager = configManager;
    this.layoutResourceProcessor = layoutResourceProcessor;
    this.dataBindingModuleLookup = dataBindingModuleLookup;
    this.modelWriter = modelWriter;
  }

  void process(RoundEnvironment roundEnv) {
    Set<? extends Element> dataBindingLayoutPackageElements =
        roundEnv.getElementsAnnotatedWith(EpoxyDataBindingLayouts.class);

    for (Element packageElement : dataBindingLayoutPackageElements) {
      List<LayoutResource> layoutResources = layoutResourceProcessor
          .getLayoutsInAnnotation(packageElement, EpoxyDataBindingLayouts.class);

      // Get the module name after parsing resources so we can use the resource classes to figure
      // out the module name
      String moduleName = dataBindingModuleLookup.getModuleName(packageElement);

//      for (LayoutResource layoutResource : layoutResources) {
//        modelInfoList.add(new DataBindingModelInfo(typeUtils, elementUtils, layoutResource,
//            moduleName));
//      }
    }
  }

  /**
   * True if databinding models have been parsed and are waiting for the DataBinding classes to be
   * generated before they can be written.
   */
  boolean hasModelsToWrite() {
    return !modelInfoList.isEmpty();
  }

  boolean isDataBindingClassesGenerated() {
    for (DataBindingModelInfo modelInfo : modelInfoList) {
      // This should be called in the last round of processing so the data binding classes will
      // have been generated by now and we can parse them to get the databinding variables
      Element dataBindingClassElement = modelInfo.getDataBindingClassElement();
      if (dataBindingClassElement == null) {
        return false;
      }
    }

    return true;
  }

  List<DataBindingModelInfo> resolveDataBindingClassesAndWriteJava() {
    for (DataBindingModelInfo modelInfo : modelInfoList) {
      Element dataBindingClassElement = modelInfo.getDataBindingClassElement();
      if (dataBindingClassElement == null) {
        errorLogger.logError(
            "Unable to find databinding class for layout %s, so an Epoxy model could not be "
                + "generated.",
            modelInfo.getLayoutResource().resourceName);
        continue;
      }

      // This should be called in the last round of processing so the data binding classes will
      // have been generated by now and we can parse them to get the databinding variables
      modelInfo.parseDataBindingClass();
    }

    writeJava();

    ArrayList<DataBindingModelInfo> result = new ArrayList<>(modelInfoList);
    modelInfoList.clear();

    return result;
  }

  private void writeJava() {
    for (DataBindingModelInfo modelInfo : modelInfoList) {
      try {
        modelWriter.generateClassForModel(modelInfo);
      } catch (Exception e) {
        errorLogger.logError(e, "Error generating model classes");
      }
    }
  }
}
